package com.lagou.export;

import com.lagou.NettyConnection;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * rpc工具类
 */
public class RpcExportUtils {

    //zk ip
    public static String ZK_CONNECTION = "localhost:2181";
    //base节点
    public static String NODE_BASE = "rpc";
    //服务端节点
    public static String SERVER_NODE = "/servers";
    //分隔符
    public static String NODE_SEP = ":";
    //mysql节点
    public static String JDBC_NODE = "/mysql";


    //zk客户端
    private volatile static CuratorFramework client = null;

//    服务注册列表
    public static volatile Map<String, NettyConnection> CONNECTION_MAP = null;

    //

    /**
     * 获取注册地址列表
     */
    public static Map<String, NettyConnection> getMap() {
        if (CONNECTION_MAP != null) {
            return CONNECTION_MAP;
        }
        CONNECTION_MAP = new HashMap<>();
        List<String> allPath = null;
        try {
            allPath = RpcExportUtils.getAllPath();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }

        for (String path : allPath) {
            CONNECTION_MAP.put(path, null);
        }
        return CONNECTION_MAP;
    }

    /**
     * zk客户端
     */
    public static CuratorFramework getClient() {

        if (client != null) {
            return client;
        }

        RetryPolicy exponentialBackoffRetry = new ExponentialBackoffRetry(1000, 3);

//        开启session
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString(ZK_CONNECTION)
                .sessionTimeoutMs(50000)
                .connectionTimeoutMs(30000)
                .retryPolicy(exponentialBackoffRetry)
                .namespace(NODE_BASE)
                .build();

        client.start();

        return client;
    }

    /**
     * 暴露服务
     */
    public static void export(String ip, Integer port) {
        CuratorFramework client = getClient();

        try {

            String path = SERVER_NODE + "/" + ip + NODE_SEP + port;

            Stat stat = client.checkExists().forPath(path);
            if (stat != null) {
                client.delete().forPath(path);
            }
            //先创建服务list的node节点
            client.create().creatingParentsIfNeeded()
                    .withMode(CreateMode.EPHEMERAL)
                    .forPath(path);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 添加客户端监听器
     */
    public static void addListener(Map<String, NettyConnection> map) {

        CuratorFramework framework = getClient();

        try {

            PathChildrenCache childrenCache = new PathChildrenCache(framework, SERVER_NODE, false);

            childrenCache.start(PathChildrenCache.StartMode.NORMAL);

            childrenCache.getListenable().addListener((curatorFramework, pathChildrenCacheEvent) -> {
                String path = pathChildrenCacheEvent.getData().getPath().split("/")[2];
                if (PathChildrenCacheEvent.Type.CHILD_REMOVED.equals(pathChildrenCacheEvent.getType())) {

                    NettyConnection connection = map.get(path);
                    if (connection != null) {
                        System.out.println("--------node remove--------" + path);
                        connection.getChannelFuture().cancel(true);
                        map.remove(path);

                    }
                } else if (PathChildrenCacheEvent.Type.CHILD_ADDED.equals(pathChildrenCacheEvent.getType())) {

                    if (!map.containsKey(path)) {
                        System.out.println("--------node add--------" + path);

                        map.put(path, null);

                    }
                }
            });

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取所有路径
     */
    public static List<String> getAllPath() throws Exception {
        CuratorFramework framework = getClient();
        return framework.getChildren().forPath(SERVER_NODE);
    }

    /**
     * 记录服务响应时间
     */
    public static long recordTime(String path) {
        try {
            long currentTime = System.currentTimeMillis();
            getClient().setData().forPath(SERVER_NODE + path, (currentTime + "").getBytes());
            NettyConnection connection = CONNECTION_MAP.get(path.replace("/", ""));
            if (connection != null) {
                connection.setTimestamp(currentTime);
            }
            return currentTime;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 从zk获取jdbc参数
     */
    public static Map<String, String> getDbSource(Properties config) throws Exception {

        CuratorFramework client = getClient();
        String jdbcPath = JDBC_NODE + "/jdbc";
        String userPath = JDBC_NODE + "/user";
        String pwdPath = JDBC_NODE + "/pwd";

        if (config != null && client.checkExists().forPath(jdbcPath) == null) {
            client.create().creatingParentsIfNeeded().forPath(jdbcPath);
            client.create().creatingParentsIfNeeded().forPath(userPath);
            client.create().creatingParentsIfNeeded().forPath(pwdPath);
            client.setData().forPath(jdbcPath, config.get("jdbc-url").toString().getBytes());
            client.setData().forPath(userPath, config.get("username").toString().getBytes());
            client.setData().forPath(pwdPath, config.get("password").toString().getBytes());
        }

        Map<String, String> result = new HashMap<>();
        String url = new String(client.getData().forPath(jdbcPath));
        String user = new String(client.getData().forPath(userPath));
        String pwd = new String(client.getData().forPath(pwdPath));

        result.put("url", url);
        result.put("username", user);
        result.put("password", pwd);

        return result;
    }

    /**
     * 更新密码
     */
    public static void update(String pwd) throws Exception {
        CuratorFramework client = getClient();
        String pwdPath = JDBC_NODE + "/password";
        client.setData().forPath(pwdPath, pwd.getBytes());
    }
}
